#include "main.h"
#define N 9999
//用于保存json文件路径
char path[256];
//创建文件拼接
char sys_cmd[N];
int maxfd, listenfd, i, n, connfd;
//接受tcp的值
char buffer[N];
//读取json文件的数组
char read_json[999];
//发送接收json的tcp的线程
void *new_sock(void *args)
{
    printf("will recv new connect\n");
    int sockfd, clientfd;
    char buf[N];
    int addrlen = sizeof(struct sockaddr);
    struct sockaddr_in addr, clientaddr;
    int nbytes;
    // 1创建一个套接字--socket
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
    {
        perror("socket err");
        exit(-1);
    }

    int opt = 1;
    setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    bzero(&addr, addrlen);
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = INADDR_ANY;
    addr.sin_port = htons(6666); //新的连接用6666端口

    // 3绑定套接字--bind
    if (bind(sockfd, (struct sockaddr *)&addr, addrlen) < 0)
    {
        perror("bind err");
        exit(-1);
    }

    // 4启动监听--listen
    if (listen(sockfd, 5) < 0)
    {
        perror("listen err");
        exit(-1);
    }

    // 5接收连接--accept
    clientfd = accept(sockfd, (struct sockaddr *)&clientaddr, &addrlen);
    if (clientfd < 0)
    {
        perror("accept err");
        exit(-1);
    }

    printf("recv new client\n");

    // 6收发数据--recv/send
    while (1)
    {

        nbytes = recv(clientfd, buf, N, 0);
        if (nbytes < 0)
        {
            perror("recv err");
            exit(-1);
        }
        else if (nbytes > 0)
        {
            //写道文件里
            FILE *file = fopen("node.json", "w");
            if (NULL == file)
            {
                printf("fopen err\n");
                pthread_exit(NULL);
            }
            printf("recv %s-%d data = %s\n", inet_ntoa(clientaddr.sin_addr),
                   ntohs(clientaddr.sin_port), buf);

            ssize_t res = fwrite(buf, 1, strlen(buf), file);
            // fprintf(file, "%s\n", buf);
            if (res <= 0)
            {
                perror("res err\n");
                pthread_exit(NULL);
            }
            //刷新缓存区
            fflush(file);
            //发送断连
            send(clientfd, "client_quit", 11, 0);
            //退出循环
            break;
        }
        else
        {
            printf("peer exit\n");
            break;
        }
    }
    //返回一个字段，让客户端断开链接
    
    sleep(1);
    //退出tcp链接
    close(sockfd);

    //退出线程资源，让自己回收自己
    pthread_detach(pthread_self());
    pthread_exit(NULL);
    printf("线程已退出\n");
}

int main(int argc, char const *argv[])
{
    printf("设备搜索进程开启\n");
    printf("等待链接\n");

    //等待接受udp广播包
    int udpfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (udpfd < 0)
    {
        perror("udpfd err");
    }

    //填充信息
    struct sockaddr_in dev_addr;
    dev_addr.sin_family = AF_INET;
    dev_addr.sin_addr.s_addr = INADDR_ANY;
    dev_addr.sin_port = htons(8866);

    socklen_t addr_len = sizeof(dev_addr);
    //监听
    if (bind(udpfd, (struct sockaddr *)&dev_addr, addr_len) < 0)
    {
        perror("bind err");
        return -1;
    }
    //创建缓存去
    char buf[N] = {0};
    //创建保存ip信息的数据
    struct sockaddr_in client_addr;
    //给缓冲区清空
    memset(buf, 0, sizeof(buf));
    //循环接受数据
    while (1)
    {
        recvfrom(udpfd, buf, N, 0, (struct sockaddr *)&client_addr, &addr_len);
        if (!memcmp(buf, "connet", 6))
        {
            printf("搜索到了自己家的设备\n");
            //发送数据
            sendto(udpfd, "yes", strlen(buf), 0, (struct sockaddr *)&client_addr, addr_len);
            // printf("----------%d\n", __LINE__);
            break;
        }
        else
        {
            printf("不是自己家的设备，跳过\n");
            //跳出本次寻选，继续接收消息等待链接
            continue;
        }
    }
    // close(udpfd);
    //开启tccp链接线程

    printf("-------------%d\n", __LINE__);
    //tcp套接子
    printf("建立tcp\n");
    fd_set rdfs, rdtmp;
    //如果链接上了，建立tcp服务器
    int tcp_servefd = socket(AF_INET, SOCK_STREAM, 0);
    if (tcp_servefd < 0)
    {
        perror("tcp_servefd err");
        exit(-1);
    }
    printf("%d\n", tcp_servefd);
    //填充网络信息,用了udp的网路信息

    //设置地址重用
    int opt = 1;
    setsockopt(tcp_servefd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    //填充信息
    struct sockaddr_in tcp_addr;
    socklen_t tcp_addr_len = sizeof(tcp_addr);
    tcp_addr.sin_family = AF_INET;
    tcp_addr.sin_addr.s_addr = INADDR_ANY;
    tcp_addr.sin_port = htons(8888);
    //绑定自己的地址
    if (bind(tcp_servefd, (struct sockaddr *)&tcp_addr, tcp_addr_len) < 0)
    {
        perror("bind1 err");
        exit(-1);
    }
    //启动监听
    int ret = listen(tcp_servefd, 5);
    if (ret < 0)
    {
        perror("listen err\n");
        exit(-1);
    }
    printf("ret = %d\n", ret);
    //这里用select监听多链接
    //更新最大的文件描述符，并设置关注集
    maxfd = tcp_servefd;
    FD_ZERO(&rdfs); //清空表
    FD_SET(tcp_servefd, &rdfs);
    while (1)
    {
        // printf("-------------%d\n", __LINE__);
        rdtmp = rdfs;
        if (select(maxfd + 1, &rdtmp, NULL, NULL, NULL) < 0)
        {
            printf("select err\n");
            return;
        }
        // 遍历集合，如果服务器句柄有数据，那么代表有连接进来，把它加入描述符集并更新maxfd
        // 否则代表客户端句柄有数据，那么接收数据，如果对端退出，那么把它从描述符集清除
        for (i = 0; i < maxfd + 1; i++)
        {
            if (FD_ISSET(i, &rdtmp))
            {
                if (i == tcp_servefd)
                {
                    // 表示有第一次的TCP连接进来
                    connfd = accept(tcp_servefd, NULL, NULL);
                    printf("有新链接进来啦\n");
                    FD_SET(connfd, &rdfs);
                    if (connfd > maxfd)
                    {
                        maxfd = connfd;
                    }
                }
                else
                { // 接受客户端数据客户端有数据
                    printf("-------------%d\n", __LINE__);

                    memset(buffer, 0, N);
                    n = recv(i, buffer, N, 0);

                    if (n > 0)
                    {
                        printf("recv from %d--%s\n", i, buffer); // 记得加\n冲刷缓冲区

                        int openfd = open("node.json", O_RDWR | O_CREAT, 0666);
                        //判断read_json为空
                        int ret = read(openfd, read_json, sizeof(read_json));
                        // printf(" ret %d\n",ret);
                        if (!memcmp(buffer, "new", 3))
                        {
                            // 新建一个新的服务器，这里如果还想保证8888的端口客户端正常响应，必须开线程，否则会阻塞到新的操作中
                            pthread_t tid1;
                            pthread_create(&tid1, NULL, new_sock, NULL);
                            continue;
                        }
                        if(!memcmp(buffer,"alive",5))
                        {
                            cJSON *time = alive_time();
                            //json转字符串
                            char *alive_buf = cJSON_Print(time);
                            printf("%s\n",alive_buf);
                            send(i, alive_buf, strlen(alive_buf), 0);
                            printf("-------------%d\n", __LINE__);
                            continue;
                        }
                        if (ret == 0)
                        {

                            cJSON *root = ser_true();
                            //json转字符串
                            char *json_buf = cJSON_Print(root);
                            sleep(1);
                            //把json给他发过去
                            
                            send(i, json_buf, strlen(json_buf), 0);
                            

                        } //json不为空
                        else
                        {
                            cJSON *root = ser_false();
                            //json转字符串
                            char *json_buf = cJSON_Print(root);
                            //把json给他发过去
                            send(i, json_buf, strlen(json_buf), 0);
                        }
                    }
                    else if (n == 0)
                    {
                        FD_CLR(i, &rdfs);
                        close(i);
                    }
                    else
                    {
                    }
                }
            }
        }
    }
    close(tcp_servefd);
    //关闭套接子

    return 0;
}
